package com.taobaolite.order;

import com.taobaolite.item.Item;
import com.taobaolite.item.ItemRepository;
import com.taobaolite.order_has_items.OrderHasItems;
import com.taobaolite.order_has_items.OrderHasItemsRepository;
import com.taobaolite.transaction.Transaction;
import com.taobaolite.transaction.TransactionRepository;
import com.taobaolite.user.UserRepository;
import com.taobaolite.user.UserType;
import com.taobaolite.user.admin.Admin;
import com.taobaolite.user.admin.AdminRepository;
import com.taobaolite.user.guest.Guest;
import com.taobaolite.user.guest.GuestRepository;
import org.aspectj.weaver.ast.Or;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;

import javax.transaction.Transactional;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;

@Service
public class OrderService {
    private final OrderRepository orderRepo;
    private final UserRepository userRepo;
    private final GuestRepository guestRepo;
    private final OrderHasItemsRepository orderHasItemsRepo;
    private final ItemRepository itemRepo;
    private final AdminRepository adminRepo;
    private final TransactionRepository transactionRepo;

    @Autowired
    public OrderService(ItemRepository itemRepo, OrderRepository orderRepo, UserRepository userRepo, OrderHasItemsRepository orderHasItemsRepo, GuestRepository guestRepo, AdminRepository adminRepo, TransactionRepository transactionRepo) {
        this.orderRepo = orderRepo;
        this.userRepo = userRepo;
        this.guestRepo = guestRepo;
        this.orderHasItemsRepo = orderHasItemsRepo;
        this.itemRepo = itemRepo;
        this.adminRepo = adminRepo;
        this.transactionRepo = transactionRepo;
    }

    @Transactional
    public OrderBody createOrder(Long userId, OrderBody orderBody) {
        if (!orderBody.getUserId().equals(userId)){
            throw new ResponseStatusException(
                    HttpStatus.NOT_ACCEPTABLE,
                    "Guest with id " +
                            userId +
                            " can't add order for guest with id " +
                            orderBody.getUserId() + "."
            );
        }
        Guest guest = guestRepo.findById(userId)
                .orElseThrow(()-> new ResponseStatusException(
                        HttpStatus.NOT_FOUND,
                        "Guest with Id " +
                                userId +
                                " not exist."
                ));
        Order order = new Order(
                OrderStatus.CREATED,
                new Timestamp(System.currentTimeMillis()),
                guest
        );
        orderRepo.save(order);
        for (OrderItem orderItem : orderBody.getOrderHasItems()) {
            orderHasItemsRepo.save(new OrderHasItems(
                    order,
                    itemRepo.findById(orderItem.getItem().getItemId())
                            .orElseThrow(() -> new ResponseStatusException(
                                    HttpStatus.NOT_FOUND,
                                    "Item with id " +
                                            orderItem.getItem().getItemId() +
                                            " does not exist."
                            )),
                    orderItem.getAmount()
            ));
        }
        orderBody.setOrderId(order.getOrderId());
        return orderBody;
    }

    @Transactional
    public PutItem putStatus(Long userId, Long orderId, Short action, OrderStatus status) {
        Order order = orderRepo.findById(orderId)
            .orElseThrow(() -> new ResponseStatusException(
                    HttpStatus.NOT_FOUND,
                    "Order with Id " +
                            orderId +
                            " does not exit."
            ));
        // 校验状态正确
        if (!order.getStatus().equals(status)) {
            throw new ResponseStatusException(
                    HttpStatus.BAD_REQUEST,
                    "Order status : " +
                            order.getStatus() +
                            " does not match " +
                            status +
                            "."
             );
        }
        switch (status){
            // 以下状态下应该由用户选择action，身份校验
            case CREATED:
            case ARRIVED:
            case RECEIVED:
            case RECEIVING:
            case REFUNDING:
                if (!order.getGuest().getGuestId().equals(userId)){
                    throw new ResponseStatusException(
                            HttpStatus.NOT_ACCEPTABLE,
                            "Guest with id " +
                                    userId +
                                    " can't put order for guest with id " +
                                    order.getGuest().getGuestId() + "."
                    );
                }
                break;
            // 以下状态应该由卖家选择action，身份校验
            case PAID:
            case SHIPPED:
                boolean flag = false;
                for(Admin admin : adminRepo.findAll()) {
                    if (admin.getAdminId().equals(userId)) {
                        flag = true;
                        break;
                    }
                }
                if (!flag) {
                    throw new ResponseStatusException(
                            HttpStatus.NOT_ACCEPTABLE,
                            "Only admins could change these status. " +
                                    "User with Id " +
                                    userId +
                                    " is not an admin. "
                    );
                }
                break;
            case FAIL_PAID:
            case FAIL_SHIP:
            case FAIL_ARRIVE:
            case FAIL_RECEIVE:
            case COMPLETED:
            case REFUNDED:
                throw new ResponseStatusException(
                        HttpStatus.BAD_REQUEST,
                        "Status: "+
                                status +
                                " can not change any more."
                );
        }
        switch (status) {
            case CREATED:
                status = payAction(order, action, status);
                break;
            case PAID:
                status = shipAction(order, action, status);
                break;
            case SHIPPED:
                status = arriveAction(order, action, status);
                break;
            case ARRIVED:
                status = receiveAction(order, action, status);
                break;
            case RECEIVED:
                status = completeAction(order, action, status);
                break;
            case RECEIVING:
                status = receiveNegotiateAction(order, action, status);
                break;
            case REFUNDING:
                status = refundNegotiateAction(order, action, status);
                break;

        }
        return new PutItem(userId, orderId, action, status);
    }

    public List<OrderBody> getOrder(Long userId, Long orderId) {
        if (orderId == null) {
            return getAll(userId);
        } else {
            List<OrderBody> temp = new ArrayList<>();
            temp.add(getById(userId, orderId));
            return temp;
        }
    }

    private List<OrderBody> getAll(Long userId){
        UserType ut = userRepo.findById(userId).orElseThrow(() -> new ResponseStatusException(
                HttpStatus.NOT_FOUND,
                "User with id " + userId +" cannot be found."
        )).getType();

        List<Order> orders =  new ArrayList<>();
        List<OrderBody> orderBodies = new ArrayList<>();

        if (ut == UserType.ADMIN) {
            orders = orderRepo.findAll();
        } else {
            orders = orderRepo.findAllByUserId(userId);
        }

        for (Order order: orders) {
            orderBodies.add(new OrderBody(order));
        }
        return orderBodies;
    }

    private OrderBody getById(Long userId, Long orderId) {
        Order order = orderRepo.findByOrderId(userId,orderId)
                .orElseThrow(() -> new ResponseStatusException(
                        HttpStatus.NOT_FOUND,
                        "User with ID " +
                                userId +
                        " Order with ID " +
                                orderId +
                                " dose not exit. " +
                                "Please check database."
                ));
        return new OrderBody(order);

    }

    // putOrder调用的私有函数：
    // 暂时缺少action完成的校验内容， 一下action函数仅提供setStatus功能
    private OrderStatus refundNegotiateAction(Order order, Short action, OrderStatus status) {
        if (action == 70) {
            order.setStatus(OrderStatus.COMPLETED);
            return OrderStatus.COMPLETED;
        } else if (action == 71) {
            order.setStatus(OrderStatus.REFUNDED);
            // 资金从admin到guest
            transactionRepo.save(new Transaction(
                    adminRepo.findAll().get(0).getUser(),
                    order.getGuest().getUser(),
                    new Timestamp(System.currentTimeMillis()),
                    getTotal(order)
            ));
            return OrderStatus.REFUNDED;
        }
        else {
            throw new PutOrderException(status, action);
        }
    }

    private OrderStatus receiveNegotiateAction(Order order, Short action, OrderStatus status) {
        if (action == 50) {
            order.setStatus(OrderStatus.FAIL_RECEIVE);
            // 资金从admin到guest
            transactionRepo.save(new Transaction(
                    adminRepo.findAll().get(0).getUser(),
                    order.getGuest().getUser(),
                    new Timestamp(System.currentTimeMillis()),
                    getTotal(order)
            ));
            return OrderStatus.FAIL_RECEIVE;
        } else if (action == 51) {
            order.setStatus(OrderStatus.RECEIVED);
            return OrderStatus.RECEIVED;
        }
        else {
            throw new PutOrderException(status, action);
        }
    }

    private OrderStatus completeAction(Order order, Short action, OrderStatus status) {
        if (action == 60) {
            order.setStatus(OrderStatus.REFUNDING);
            return OrderStatus.REFUNDING;
        } else if (action == 61) {
            order.setStatus(OrderStatus.COMPLETED);
            // 资金从admin到store
            for (OrderHasItems orderHasItems : order.getItems()) {
                new Transaction(adminRepo.findAll().get(0).getUser(),
                        orderHasItems.getOrderHasItemsPK().getItem().getStore().getUser(),
                        new Timestamp(System.currentTimeMillis()),
                        orderHasItems.getOrderHasItemsPK().getItem().getPrice() *
                            orderHasItems.getAmount()
                );
            }
            return OrderStatus.COMPLETED;
        }
        else {
            throw new PutOrderException(status, action);
        }
    }

    private OrderStatus receiveAction(Order order, Short action, OrderStatus status) {
        if (action == 40) {
            order.setStatus(OrderStatus.RECEIVING);
            return OrderStatus.RECEIVING;
        } else if (action == 41) {
            order.setStatus(OrderStatus.RECEIVED);
            return OrderStatus.RECEIVED;
        }
        else {
            throw new PutOrderException(status, action);
        }
    }

    private OrderStatus arriveAction(Order order, Short action, OrderStatus status) {
        if (action == 30) {
            order.setStatus(OrderStatus.FAIL_ARRIVE);
            // 资金从admin到guest
            transactionRepo.save(new Transaction(
                    adminRepo.findAll().get(0).getUser(),
                    order.getGuest().getUser(),
                    new Timestamp(System.currentTimeMillis()),
                    getTotal(order)
            ));
            return OrderStatus.FAIL_ARRIVE;
        } else if (action == 31) {
            order.setStatus(OrderStatus.ARRIVED);
            return OrderStatus.ARRIVED;
        }
        else {
            throw new PutOrderException(status, action);
        }
    }

    private OrderStatus shipAction(Order order, Short action, OrderStatus status) {
        if (action == 20) {
            order.setStatus(OrderStatus.FAIL_SHIP);
            // 资金从admin到guest
            transactionRepo.save(new Transaction(
                    adminRepo.findAll().get(0).getUser(),
                    order.getGuest().getUser(),
                    new Timestamp(System.currentTimeMillis()),
                    getTotal(order)
            ));
            return OrderStatus.FAIL_SHIP;
        } else if (action == 21) {
            order.setStatus(OrderStatus.SHIPPED);
            return OrderStatus.SHIPPED;
        }
        else {
            throw new PutOrderException(status, action);
        }
    }

    private OrderStatus payAction(Order order, Short action, OrderStatus status) {
        if (action == 10) {
            order.setStatus(OrderStatus.FAIL_PAID);
            return OrderStatus.FAIL_PAID;
        } else if (action == 11) {
            order.setStatus(OrderStatus.PAID);
            // 库存减少
            for (OrderHasItems orderHasItems : order.getItems()) {
                orderHasItems.getOrderHasItemsPK().getItem().setAmount(
                        orderHasItems.getOrderHasItemsPK().getItem().getAmount()
                        - orderHasItems.getAmount()
                );
            }
            // 资金从admin到guest
            transactionRepo.save(new Transaction(
                    order.getGuest().getUser(),
                    adminRepo.findAll().get(0).getUser(),
                    new Timestamp(System.currentTimeMillis()),
                    getTotal(order)
            ));
            return OrderStatus.PAID;
        }
        else {
            throw new PutOrderException(status, action);
        }
    }

    // 获取order的total的函数
    private Double getTotal (Order order) {
        Double total = 0D;
        for (OrderHasItems orderHasItems : order.getItems()) {
            total += orderHasItems.getAmount()*
                    orderHasItems.getOrderHasItemsPK().getItem().getPrice();
        }
        return total;
    }
}
